# frozen_string_literal: true

class VariableType < ApplicationRecord
  module Status
    extend ConstantEnum

    CREATED = 'created'
    DEPLOYED = 'deployed'
  end

  module Scalar
    extend ConstantEnum

    INTEGER = 'integer'
    STRING = 'string'
    BOOLEAN = 'boolean'
    FLOAT = 'float'
    ARRAY = 'array'
    HASH = 'hash'
  end

  has_many :type_methods, dependent: :destroy

  before_create :init_status

  validates :name, presence: true, uniqueness: { case_sensitive: true }
  validates :klass, presence: true
  validates :status, inclusion: { in: Status.values }, allow_blank: true

  scope :filter_name, ->(name) { where('name like ?', "%#{name}%") if name.present? }
  scope :filter_status, ->(status) { where(status: status) if status.present? }

  PRE_TYPES = {
    Scalar::STRING => {
      klasses: [String]
    },
    Scalar::INTEGER => {
      klasses: [Integer],
      regex: /\A[+\-]?(0|[1-9]\d*)\z/
    },
    Scalar::FLOAT => {
      klasses: [Integer, Float],
      regex: /\A[=\-]?(0|[1-9]\d*)(\.\d+)?\z/
    },
    Scalar::BOOLEAN => {
      values: [true, false, 'true', 'false', 'TRUE', 'FALSE', 'True', 'False']
    },
    Scalar::ARRAY => {
      klasses: [Array],
      block: ->(value) { value.is_a?(String) && JSON.parse(value).is_a?(Array) }
    },
    Scalar::HASH => {
      klasses: [Hash],
      block: ->(value) { value.is_a?(String) && JSON.parse(value).is_a?(Hash) }
    }
  }.freeze

  def parse(value)
    return nil if value.nil?

    raise 'Parse value error, unsupported' unless validate?(value)

    case name
    when 'Integer'
      value.to_i
    when 'Float'
      value.to_f
    when 'Boolean'
      value.casecmp('true').zero?
    when 'String'
      value.to_s
    when Array.name, Hash.name
      JSON.parse(value)
    else
      value
    end
  end

  def validate?(value)
    return validate_pre_type?(value, PRE_TYPES[name]) if PRE_TYPES.key?(name)

    false
  rescue StandardError => e
    logger.error e
    false
  end

  private

  def init_status
    self.status = Status::CREATED
  end

  def validate_pre_type?(value, params)
    result = params[:klasses]&.any? { |klass| value.is_a?(klass) } ||
             (value&.is_a?(String) && params[:regex]&.match?(value)) ||
             params[:values]&.include?(value) || params[:block]&.call(value)
    result.nil? ? false : result
  end
end
